package com.zyb.ext.mymvc.servlet;

import com.zyb.ext.ioc.utils.ClazzUtils;
import com.zyb.ext.mymvc.annotate.ExtController;
import com.zyb.ext.mymvc.annotate.ExtRequestMapping;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义SpringMVC dispatcher
 */
@Slf4j
public class ExtDispatcherServlet extends HttpServlet {

    //存储beanId和对象的对应关系，key：beanId，value：为对应的对象
    private Map<String, Object> beanIdBeans = null;
    //存储url和对象的对应关系，key：完整的url，value：为对应的对象
    private Map<String, Object> urlBeans = null;
    //存储url和方法的对应关系，key：完整的url，value：对应的具体方法
    private Map<String, Method> urlMethod = null;

    @Override
    public void init() {
        try {
            beanIdBeans = new ConcurrentHashMap<>();
            urlBeans = new ConcurrentHashMap<>();
            urlMethod = new ConcurrentHashMap<>();
            //获取指定包下面的所有class文件
            List<String> clazzName = ClazzUtils.getClazzName("com.zyb.mymvc", true);
            if (clazzName == null && clazzName.isEmpty()) {
                throw new RuntimeException("under this packageName is not found class info!");
            }
            //遍历所有的class文件
            for (String className : clazzName) {
                Class<?> aClass = Class.forName(className);
                //判断class文件是否有ExtController注解
                ExtController declaredAnnotation = aClass.getDeclaredAnnotation(ExtController.class);
                if (declaredAnnotation == null) {
                    continue;
                }
                String beanId = ClazzUtils.getBeanIdByClass(aClass);
                Object instance = aClass.newInstance();
                //维护beanId和对应的关系
                beanIdBeans.put(beanId, instance);

                //维护url和对象、url和方法的对应关系
                if (handleMapping(aClass, instance)) continue;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 维护url和对象、url和方法的对应关系
     * @param aClass
     * @param instance
     * @return
     */
    private boolean handleMapping(Class<?> aClass, Object instance) {
        //判断类上是否有ExtRequestMapping注解
        ExtRequestMapping classRequestMapping = aClass.getDeclaredAnnotation(ExtRequestMapping.class);
        //如果存在ExtRequestMapping注解，拿出url
        String classUrl = "";
        if (classRequestMapping != null) {
            classUrl = classRequestMapping.value();
        }
        //获取类自身的所有方法
        Method[] declaredMethods = aClass.getDeclaredMethods();
        if (declaredMethods == null && declaredMethods.length <= 0) {
            return true;
        }
        StringBuffer fullUrl = null;
        for (Method declaredMethod : declaredMethods) {
            //判断方法上是否有ExtRequestMapping注解
            ExtRequestMapping declaredAnnotation1 = declaredMethod.getDeclaredAnnotation(ExtRequestMapping.class);
            if (declaredAnnotation1 == null) {
                continue;
            }
            fullUrl = new StringBuffer();
            //拼接成完整的url
            fullUrl.append(classUrl).append(declaredAnnotation1.value());
            //维护url和方法的对应关系
            urlMethod.put(fullUrl.toString(), declaredMethod);
            //维护url和对象的对应关系
            urlBeans.put(fullUrl.toString(), instance);
        }
        return false;
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            String prefix = "/";
            String suffix = ".jsp";
            String requestURI = req.getRequestURI();
            Object obj = urlBeans.get(requestURI);
            Method method = urlMethod.get(requestURI);
            if (obj == null || method == null) {
                //重定向到错误页面
                resp.sendRedirect(prefix + "error" + suffix);
                return;
            }
            Object invoke = method.invoke(obj);
            //判断是否是String类型,表示返回页面
            if (invoke instanceof String) {
                req.getRequestDispatcher(prefix + invoke + suffix).forward(req, resp);
            } else {
                //表示返回数据，直接打印结果
                resp.getWriter().print(invoke);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void destroy() {

        log.info("销毁SpringMVC服务...");
        urlMethod.clear();
        urlMethod = null;

        urlBeans.clear();
        urlBeans = null;

        beanIdBeans.clear();
        beanIdBeans = null;
    }
}
